// s.r(t), s.d(t, { createModelMesh: () => M })
import { ModelLoadProgressMessage } from "../message/model.message"
import RenderLayers, { RenderLayer } from "../utils/RenderLayers"
import Chunk from "../other/321-8511"
import { MeshCreationException } from "../exception/mesh.exception"
import * as THREE from "three"
import OrderedMap from "../utils/OrderedMap"
import FloorMesh from "./FloorMesh"
import chunkUtil from "../utils/chunk.util"
import Logger from "../utils/Logger"
import configConst from "../constant/config.const"
import { chunkType } from "../enum/chunk.enum"
import ModelMeshBase from "./ModelMeshBase"
import DamLoader from "../loader/DamLoader"
const x = new Logger("mesh")
class ModelMeshDam extends ModelMeshBase {
  renderLayer: RenderLayer
  floorMeshes: OrderedMap
  built: boolean
  signedUrls: any
  constructor(e, t, s = RenderLayers.ALL) {
    super(),
      (this.renderLayer = s),
      (this.floorMeshes = new OrderedMap(e => e.meshGroup)),
      (this.built = !1),
      (this.uuid = e),
      (this.name = `ModelMesh:${e}`),
      (this.signedUrls = t),
      (this.layers.mask = s.mask)
  }
  dispose() {
    this.floorMeshes.mapElements(e => {
      e.dispose(), this.remove(e)
    }),
      this.floorMeshes.clear(),
      (this._chunks.length = 0),
      (this.built = !1)
  }
  reset() {
    this.floorMeshes.mapElements(e => {
      e.reset(), this.remove(e)
    }),
      (this._chunks.length = 0),
      (this.built = !1)
  }
  async load(e: any = {}) {
    const { roomMeshData: t } = e

    let s = e.chunks
      ? e.chunks
      : await class ModelLoader {
          static async load(e, t, s, r = 0) {
            const i = new DamLoader()
            configConst.switchConfig.highQualityDam && 0 === r && (r = 1)
            const a = (
              t.meshUrls
                ? t.meshUrls
                : [
                    { suffix: "_50k", extension: "dam" },
                    { suffix: "", extension: "dam" }
                  ]
            )[r]
            if (!a) return Promise.reject("No suitable model file found...")
            const { url: o, suffix: n, extension: h } = a,
              d = o ? await o.get() : `${e}${n}.${h}`
            return i.load(d, t, s).catch(() => this.load(e, t, s, ++r))
          }
        }
          .load(this.uuid, this.signedUrls, e.onProgress)
          .catch(e => {
            x.error(e)
            const t = e instanceof Error ? e.message : "Failed to load model mesh"
            throw new MeshCreationException(t)
          })
    if (0 === s.length) {
      x.warn("No geometry found for model, loading faux geometry, disabling outside view-mode")
      const e = new THREE.PlaneBufferGeometry(5, 5, 1, 1)
      e.rotateX(-Math.PI / 2), e.computeBoundingBox()
      const t = new Chunk(0, 0, e)
      t.forEachMaterial(e => (e.visible = !1)), (s = [t])
    }
    s.forEach((e, t) => {
      this.addChunk(e)
    }),
      this.build(t)
  }
  addChunk(e) {
    const t = this.getOrCreateFloorMesh(e.meshGroup)
    this._chunks.push(e), t.addChunk(e)
  }
  build(e) {
    if (this.built) throw new Error("build() should only be called once")
    let t = 0,
      s = 0
    for (const e of this.floorMeshes) {
      this.add(e)
      for (const s of e.roomMeshes) s.build(), s.geometry.computeBoundsTree && !s.geometry.boundsTree && s.geometry.computeBoundsTree(), t++
      e.build(), s++
    }
    x.debug(`FloorMeshes: ${s} RoomMeshes: ${t} Chunks: ${this._chunks.length}`), this.boundingBox.makeEmpty()
    for (const e of this.floorMeshes) this.boundingBox.union(e.boundingBox)
    ;(this.size = this.boundingBox.getSize(this.size)),
      (this.center = this.boundingBox.getCenter(this.center)),
      (e.floors = new Set(this.floorMeshes)),
      (e.rooms = this.roomMeshes),
      e.commit(),
      (this.built = !0)
  }
  get roomMeshes() {
    const e = new Set()
    for (const t of this.floorMeshes) for (const s of t.roomMeshes) e.add(s)
    return e
  }
  setSide(e) {
    for (const t of this.floorMeshes) for (const s of t.roomMeshes) this.setSideRecursively(s, e)
  }
  initTextureLoader(e, t, s, r) {
    e.setModel(chunkType.DAM, this, this.chunks, t.textures)
    const i = r.streamingTextures ? chunkUtil.getMinQuality(chunkType.DAM) : r.maxQuality
    return e.loadAll(i).then(() => {
      s()
    })
  }
  registerCollision(e) {
    e.registerMesh(this, !0)
    for (const t of this.roomMeshes) e.registerSnappingMesh(t)
  }
  onMeshIdsUpdated(e) {
    for (const t of this.floorMeshes) {
      for (const s of t.roomMeshes) (s.floorId = e.floorIds[s.meshGroup]), (s.roomId = e.roomIds.get(s.meshGroup, s.meshSubgroup) || "")
      const s = e.floorIds[t.meshGroup]
      t.floorId = s
    }
  }
  setTextureQuality(e, t, s) {
    e.setQuality(t, s)
  }
  onUpdate() {}
  setSideRecursively(e, t) {
    e instanceof THREE.Mesh && e.material && e.material instanceof THREE.MeshBasicMaterial && (e.material.side = t)
    for (const s of e.children) this.setSideRecursively(s, t)
  }
  getOrCreateFloorMesh(e) {
    let t = this.floorMeshes.get(e)
    return t || ((t = new FloorMesh(e, this.renderLayer)), this.floorMeshes.add(t), this.add(t)), t
  }
}
async function createModelMesh({ uuid: e, urls: t, renderLayer: s, engine: i, roomMeshData: a }) {
  const o = new ModelMeshDam(e, t, s)
  await o.load({
    roomMeshData: a,
    onProgress: e => {
      i.broadcast(new ModelLoadProgressMessage(e.loaded, e.total))
    }
  })
  return o
}
export default {
  createModelMesh
}
